(ns util.file
  (:use [clojure.java.io :only [file reader delete-file]])
  (:require clojure.string)
  (:import org.apache.commons.io.FileUtils))

(defn file= [f1 f2]
  (= (file f1) (file f2)))

(defn file== [f1 f2]
  (= (.getCanonicalPath (file f1)) (.getCanonicalPath (file f2))))

(defn file-basename [f]
  (.getName (file f)))

(defn file-dirname
  ([f]
    (.getParent (file f)))
  ([f n]
    (if (<= n 0)
      f
      (recur (.getParent (file f)) (dec n)))))

(defn file-name [f]
  (file-basename f))

(defn file-father [f]
  (file-dirname f))

(defn file-parent [f]
  (file-dirname f))

(defn file-abs [f]
  (.getAbsolutePath (file f)))

(defn file-relative-of [path base]
  (.. (java.io.File. base) toURI (relativize (.. (java.io.File. path) toURI)) getPath))

(defn file-extension [f]
  "Note: this returns .zip (not zip)"
  (let [name (file-name f)]
    (re-find #"\.[^.]+$" name)))

(defn file-split-path [f]
  (let [f (file f)]
    [(.getParent f) (.getName f)]))

(defn file-split-parts [f]
  (loop [f f parts nil]
    (if f
      (recur (file-dirname f) (cons (file-basename f) parts))
      parts)))

(defn file-list [d]
  (map #(.getName %) (.listFiles (file d))))

(defn file-list-full [d]
  (map #(.getPath %) (.listFiles (file d))))

(defn file-filter-type [ext files]
  (filter #(re-matches
          (java.util.regex.Pattern/compile
            (str "(?i)^.*\\." (java.util.regex.Pattern/quote ext) "$"))
          %)
    files))

(defn file-join [root & parts]
  (if root
    (.getPath (reduce #(java.io.File. %1 %2) (file root) parts))
    (apply file-join parts)))


(def ^:dynamic *file-encoding* nil)

(defn file-save [content path & parts] ; TODO: add encoding support
  (let [p (apply file-join path parts)]
    (if *file-encoding*
      (spit p content :encoding *file-encoding*)
      (spit p content))
    p))

(defn file-append [content path & parts] ; TODO: add encoding support
  (let [p (apply file-join path parts)]
    (spit p content :append true)
    p))

(defn file-append-line [line path & parts]
  (let [line (str line (System/getProperty "line.separator"))]
    (apply file-append line path parts)))

(defn file-clear [path & parts]
  (apply file-save "" path parts))

(defn file-lines [path & parts]
  (line-seq (reader (apply file-join path parts))))

(defn file-lines-all [path & parts]
  "Similar to file-lines, but read all lines once and close the file (so the handler is release and file can be deleted/moved)"
  (with-open [r (reader (apply file-join path parts))]
    (doall (line-seq r))))

(defn file-update [f path]
  (file-save (f (slurp path)) path))

(defn file-replace-all [path match replacement]
  (file-update #(clojure.string/replace % match replacement) path))

(defn file-replace-first [path match replacement]
  (file-update #(clojure.string/replace-first % match replacement) path))

(defn file-touch [path & parts]
  (let [f (file (apply file-join path parts))]
    (if (.exists f)
      (.setLastModified f (System/currentTimeMillis))
      (.createNewFile f))))

(defn file-exists? [f]
  {:pre [f]}
  (.exists (file f)))

(defn is-file? [path]
  {:pre [path]}
  (.isFile (file path)))

(defn is-dir? [path]
  {:pre [path]}
  (.isDirectory (file path)))

(defn file-delete [f]
  (delete-file f))

(defn file-delete-tree [d]
  (FileUtils/deleteDirectory (file d)))

(defn file-safe-delete [p]
  (when (file-exists? p)
    (if (is-dir? p)
      (file-delete-tree p)
      (file-delete p))))

(defn file-copy [src dest]
  (FileUtils/copyFile (file src) (file dest)))

(defn file-copy-tree [src dest]
  (FileUtils/copyDirectory (file src) (file dest)))

(defn file-copy-to [src dest]
  (FileUtils/copyFileToDirectory (file src) (file dest)))

(defn file-copy-tree-to [src dest]
  (FileUtils/copyDirectoryToDirectory (file src) (file dest)))

(defn file-move [src dest]
  (if (is-dir? src)
    (FileUtils/moveDirectory (file src) (file dest))
    (FileUtils/moveFile (file src) (file dest))))

(defn file-move-to [src dest]
  (FileUtils/moveToDirectory (file src) (file dest) false))

(defn file-rename [src dest]
  (let [dir (file-dirname src)
        dest (file-join dir dest)]
    (file-move src dest)))

(defn mkdir [path & parts]
  (FileUtils/forceMkdir (file (apply file-join path parts))))

(defn mktemp
  ([]
    (mktemp "mekit-"))
  ([prefix]
    (mktemp prefix ""))
  ([prefix suffix]
    (java.io.File/createTempFile prefix suffix))
  ([prefix suffix dir]
    (java.io.File/createTempFile prefix suffix (file dir))))

(defn file-size [path & parts]
  (.length (file (apply file-join path parts))))

(defn dir-size [path & parts]
  (FileUtils/sizeOfDirectory (file (apply file-join path parts))))

(def line-separator (System/getProperty "line.separator"))

